---
title: Filter Expressions
description: Filter Expressions
keywords:
  - electrodb
  - docs
  - concepts
  - dynamodb
  - FilterExpression
  - ConditionExpression
layout: ../../../layouts/MainLayout.astro
---

Building thoughtful indexes can make queries simple and performant, though sometimes you need to apply further filters to your query.

## FilterExpressions

Below demonstrates how to add a `FilterExpression` to Dynamo's DocumentClient directly alongside how you would accomplish the same using the `where` method.

### Example

```typescript
animals.query
  .exhibit({ habitat: "Africa" })
  .where(
    ({ isPregnant, offspring }, { exists, eq }) => `
    ${eq(isPregnant, true)} OR ${exists(offspring)}
  `,
  )
  .go();
```

### Equivalent Parameters

```json
{
  "KeyConditionExpression": "#pk = :pk and begins_with(#sk1, :sk1)",
  "TableName": "zoo_manifest",
  "ExpressionAttributeNames": {
    "#isPregnant": "isPregnant",
    "#offspring": "offspring",
    "#pk": "gsi1pk",
    "#sk1": "gsi1sk"
  },
  "ExpressionAttributeValues": {
    ":isPregnant0": true,
    ":pk": "$zoo#habitat_africa",
    ":sk1": "$animals_1#enclosure_"
  },
  "IndexName": "gsi1pk-gsi1sk-index",
  "FilterExpression": "#isPregnant = :isPregnant0 OR attribute_exists(#offspring)"
}
```

[![Try it out!](https://img.shields.io/badge/electrodb-try_out_this_example_›-%23f9bd00?style=for-the-badge&logo=amazondynamodb&labelColor=1a212a)](https://electrodb.fun/?ssl=91&ssc=8&pln=86&pc=1#code/JYWwDg9gTgLgBAbwKIDsbBgTwDRwMoCmUAbsAMYEC+cAZlBCHAEQEA2BZM9AJgEZMAoAWQgoAzvACGKUJNZi4AXjgoCAdzip0WABQIBcOCAjc2ALkQHD1sUVIULTAF4QITbFetwCaDJkfSsvLuntbERGLAoo4AjCGGlB6GkjBcwLwArjAEYhb6XgAWkrwYKXmhhlhgBBYA2kwAgnTkku7MDVCc5G1MAEqSwCgAYtA5MEwAuklecFAEAI4ZwHPcFlwZBNMJW94oZKwQYhlz5TNwVTXMElCDAObx1omhgSBypzMXjtd3D15zi8sCKtzlANjsnl4UJIQJd8h9MNUvmkUPdQhDrKxJBIhkD3l5PldkaivOjDJjsUCAEL+SxnAlMb4owQknbcaS3IgQDK5WnwxHMXiudjSX5wUlwYBiAAKc1uULQeOs9MFEGFKGZjx2JVgBTZNLh+IRlwZRI121C3GABBgisqRq+1tFhgwBBAPJNNyZaJ2EBoNDEYE9t1t53tzFYkvGO2d2TdIbt-KYrzATusgYg1VgVp5BrOKmhsIqefpjOJefN5cM2pgusk+qLdLDHp+Dc1rcMKAg2XjfONpbNecoraHZxHbYrEpQpgAHjlFQGOKAfCGwABrHuGGhW1jAphr1NeETgQ4uupMF5ySbRsXXsTr3nlrdsXd3g-WI+QSLduD1KEwyZFmOE6GAQ04FOkGAhoMM6OLckQxGuAC0cHADEd6IdBoGpmuG60Nuu4oQhq5voYH4nt+9RFCUMApFegG3veuZnE+O6wfBr7XoeDCfqeP4sHsBxHHMPTkjAOLcABo7elYTyJIgNG8OwjguBAAD6rwyDQYxMJQACUADcQgXvIAB0ixEJgVgmaB4HUXoVGlDa7TNGQrR6VZagFEQBA6HokoygQcrSDAuC+v6gZ3HJCCgZGYi4AselKAAfHAAAGoQACTRfMOj+bK8ohSCGy6dQADyvRwFlMUSGIOhhQGQYlVYqW6VZtwQDoBkCEAA)

## Where Clause

The `where()` method allow you to write a `FilterExpression` or `ConditionExpression` without having to worry about the complexities of expression attributes. To accomplish this, ElectroDB injects an object `attributes` as the first parameter to all Filter Functions, and an object `operations`, as the second parameter. Pass the properties from the `attributes` object to the methods found on the `operations` object, along with inline values to set filters and conditions.

> Provided `where` callbacks must return a string. All method on the `operation` object all return strings, so you can return the results of the `operation` method or use template strings compose an expression.

### Examples

#### A single filter expression

```typescript
animals.query
  .exhibit({ habitat: "Africa", enclosure: "5b" })
  .where((attr, op) => op.eq(attr.dangerous, true))
  .go();
```

#### A single filter expression with destructuring

```typescript
animals.query
  .exhibit({ habitat: "Africa", enclosure: "5b" })
  .where(({ dangerous }, { eq }) => eq(dangerous, true))
  .go();
```

#### Multiple filter expressions

```typescript
animals.query
  .exhibit({ habitat: "Africa", enclosure: "5b" })
  .where(
    (attr, op) => `
    ${op.eq(attr.dangerous, true)} AND ${op.notExists(attr.lastFed)}
  `,
  )
  .go();
```

#### Chained usage (implicit AND)

```typescript
animals.query
  .habitats({ habitat: "Africa", enclosure: "5b" })
  .where((attr, op) => `
    ${op.eq(attr.dangerous, true)} OR ${op.notExists(attr.lastFed)}
  `)
  .where(({ birthday }, { between }) => {
    const today = Date.now();
    const lastMonth = today - 1000 * 60 * 60 * 24 * 30;
    return between(birthday, lastMonth, today);
  })
  .go();
```

#### "dynamic" filtering

```typescript
type GetAnimalOptions = {
  habitat: string;
  keepers: string[];
};

function getAnimals(options: GetAnimalOptions) {
  const { habitat, keepers } = options;
  return animals.query.exhibit({ habitat })
  .where(({ keeper }, { eq }) => {
    return keepers.map((name) => eq(keeper, name)).join(' AND ');
  }).go()
}

const { data, cursor } = await getAnimals({
  habitat: "RainForest",
  keepers: ["Joe Exotic", "Carol Baskin"],
});
```

## Operations

[![Try it out!](https://img.shields.io/badge/electrodb-try_out_this_example_›-%23f9bd00?style=for-the-badge&logo=amazondynamodb&labelColor=1a212a)](https://electrodb.fun/?#code/JYWwDg9gTgLgBAbwKIDsbBgTwL5wGZQQhwBEApgDZkDGMhAJgEYkBQL1EKAzvABYCGjDPxhc4AXjgBtEgEECwavxIAaUrKi1Fq0gCV+wFADFoZHiQC6cfmI7cYAbhZYwZOAAlBw+JJdkIeHACQjAiXFIoAK4gjGRQFk7snDzWKKD8FGKSKGQA7nCo6FgAFAgscHAgEPSUAFyI5RVNXHEAbopk9SQAXhAQqo1NcGRoGJhd-GkgGVwDQxWtcVzAnF0AjHNw2CqNInTAjJEwZvVlQ8Hep4NNfvUXoaI783BQZACOkcCv9PV0kWRPJrba4jagUCBcSKvK7PW6kHhQQwAc02FWBQ0m6QoMPmcJICORqKarw+XzIPzgfwB13RTRQ-BAnQasMwri6BJQKJpgIq9EmSLiEEiXBxQzxjD6VEmRNpFWAXAACq8kfS0KKbqymSQJRApShWENZXAhLBeHzxszcZqulEYnEDUCeXB6MAyDB1RU8S0YES5ccQCL4ftOQ60U6ANZkMiuKAeynWoOIkPc66LY5J-iIyZxr3BrmGp0BPBcMBJpE5hMkCjyn1Ov1kANxz2V6ZgX1NUsQGPoE6W54VemMptWtmJwnXZ5G54mmBm-gWs79sWVjn5pdbOvzFAQY7D5ej-F50P97AToFn08F64UGwwIzkisH1fHo03nj3+gAIQXZ9zZZfTpvjASCtBkkQiCsKCPlqz4pkMQEgWBEGcN+0HskeNKNLShg1AAHr2i4VCWNCgCMcZgOGe74K6FAUiQFHtkMHDgBCGBMjImLTBQliblOcBcJRfb9ngNF0QJjFNMxkDLLu0gkIOZCWBecFNGQuG8AcGBxjhaldEiyxrBRAC0+nAGsAlGTpuHthRVEiZQdGmYZ4YSRUUmsbJMj3CIPEXpuAl2aJekGeJm5MUQ0lsfUMiguCkKvDoVa3h+SmTipwLbIgoSMFQXS9BAAD60xpHgZg+tgACUiR+HAADibqyFMGQAPJgOgyQSJa3nuh4XgPE4FSRtGSz1KuUgJCw2CJHgkQoFonBwAKMCNViXDFF27XcPU9XLU1FCtZtXAVZadgpAgQR9SIahDTGYi4JIG2QVwA0vG6UIoKkq0AHQfHEmBfWpGkhKUF0hCIWwVY0X25LwcRkMUIM3XEG6IMMbwQxIAB8QmvDA71wEjUBcF9rYIwpx3iNj7zFITajkxVX0AFYQIYxQAORwM1uhwGzVVYQzSIQMUkOniwS0rVxa2Lt1XT6IYJivOYgKE4GUjXCQABSEBuEguE7toTokAAwpmupwJ+NjhoYDoWDslUOEAA)

The `attributes` object contains every Attribute defined in the Entity's Model. The `operations` object contains the following methods:

| operator      | example                           | result                              |
| ------------- | --------------------------------- | ----------------------------------- |
| `eq`          | `eq(rent, maxRent)`               | `#rent = :rent1`                    |
| `ne`          | `ne(rent, maxRent)`               | `#rent <> :rent1`                   |
| `gte`         | `gte(rent, value)`                | `#rent >= :rent1`                   |
| `gt`          | `gt(rent, maxRent)`               | `#rent > :rent1`                    |
| `lte`         | `lte(rent, maxRent)`              | `#rent <= :rent1`                   |
| `lt`          | `lt(rent, maxRent)`               | `#rent < :rent1`                    |
| `begins`      | `begins(rent, maxRent)`           | `begins_with(#rent, :rent1)`        |
| `exists`      | `exists(rent)`                    | `attribute_exists(#rent)`           |
| `notExists`   | `notExists(rent)`                 | `attribute_not_exists(#rent)`       |
| `contains`    | `contains(rent, maxRent)`         | `contains(#rent = :rent1)`          |
| `notContains` | `notContains(rent, maxRent)`      | `not contains(#rent = :rent1)`      |
| `between`     | `between(rent, minRent, maxRent)` | `(#rent between :rent1 and :rent2)` |
| `size`        | `size(rent)`                      | `size(#rent)`                       |
| `type`        | `type(rent, 'N')`                 | `attribute_type(#rent, 'N')`        |
| `name`        | `name(rent)`                      | `#rent`                             |
| `value`       | `value(rent, maxRent)`            | `:rent1`                            |
| `escape`      | `escape(123)`, `escape('abc')`    | `#123`, `#abc`                      |
| `field`       | `field('field_name')`             | `:field_name`                       |

### ElectroDB Functions

The filter functions available above all come from the list of functions supported by DynamoDB directly. ElectroDB offers a few functions that offer some additional convenience when creating Filter Expressions.

> Note: If ElectroDB ever lags behind in implementing FilterExpression functions, you can use `value()`, `name()`, `escape()`, and/or `field()` to simply template out the implementation!

#### Name

The `name()` function allows you to create a reference to an attribute's name, which can be useful to create filters referencing an attribute as it is currently stored. The function will add a `ExpressionAttributeNames` property for this record and return the partial expression for use in your filter.

This example demonstrates how you might find animals that were last fed by someone other than their "keeper".

```typescript
animals.query
  .exhibit({ habitat: "Africa", enclosure: "5b" })
  .where(
    ({ lastFedBy, keeper }, { name }) => `
    ${name(lastFedBy)} != ${name(keeper)}
  `,
  )
  .go();
```

#### Value

The `value()` function can be paired with `name()` as convenience when building unique filters. The `value()` method is passed an attribute (used to enforce type), and a value for that attribute. ElectroDB will add a `ExpressionAttributeValues` property for this value, and return a reference to that value that can be used in your `FilterExpression`.ExpressionAttributeValues

This example shows how you can implement the `eq` operation without using the ElectroDB `eq` function

```typescript
animals.query
  .exhibit({ habitat: "Africa", enclosure: "5b" })
  .where(
    ({ keeper }, { name, value, eq }) => `
    ${name(keeper)} = ${value(keeper, "Tiger King")}
  `,
  )
  .go();
```

#### Escape

The `escape()` method allows you to provide any primitive to an expression without the need for a specific attribute. In many ways, this function is like a less restrictive version of `value()`. This can be useful when using static values and/or when creating custom FilterExpressions

This example shows how you might use escape to apply a filter against the `size()` of an attribute.

```typescript
animals.query
  .exhibit({ habitat: "Africa", enclosure: "5b" })
  .where(
    ({ diet }, { size, escape }) => `
    ${size(diet)} > ${escape(2)}
  `,
  )
  .go();
```

#### Field

The `field()` method allows you to provide and reference a field value that is not present in your model. This method is similar to `escape()` but is used for field names.

You can use both `escape()` and `field()` to template _any_ filter that DynamoDB supports.

```typescript
animals.query
  .exhibit({ habitat: "Africa", enclosure: "5b" })
  .where(
    (_, { field, escape }) => `
    contains(${field("gsi1sk")}, ${escape("value")})
  `,
  )
  .go();
```

## Advanced Usage

### Where with Complex Attributes

ElectroDB supports using the `where()` method with DynamoDB's complex attribute types: `map`, `list`, and `set`. When using the injected `attributes` object, simply drill into the attribute itself to apply your update directly to the required object.

The following are examples on how to filter on complex attributes:

#### Filtering on a `map` attribute

```typescript
animals.query
  .farm({ habitat: "Africa" })
  .where(({ veterinarian }, { eq }) => eq(veterinarian.name, "Herb Peterson"))
  .go();
```

[![Try it out!](https://img.shields.io/badge/electrodb-try_out_this_example_›-%23f9bd00?style=for-the-badge&logo=amazondynamodb&labelColor=1a212a)](https://electrodb.fun/?#code/JYWwDg9gTgLgBAbwKIDsbBgTwDRwMoCmUAbsAMYEC+cAZlBCHAEQEA2BZM9AJgEZMAoAWQgoAzvACGKUJNZi4AXjgoCAdzip0WABQIBcOCAjc2ALkQHD1sUVIULTAF4QITbFetwCaDJkfSsvLuntbERGLAoo4AjCGGlB6GkjBcwLwArjAEYhb6XgAWkrwYKXmhhlhgBBYA2kwAgnTkku7MDVCc5G1MAEqSwCgAYtA5MEwAuklecFAEAI4ZwHPcFlwZBNMJW94oZKwQYhlz5TNwVTXMElCDAObx1omhgSBypzMXjtd3D15zi8sCKtzlANjsnl4UJIQJd8h9MNUvmkUPdQhDrNxpLciBAMrlLGdPsxeK52NJfnB0YZgGIAApzW5QtDvLxEpgkiBklCCLxUuAlWAFTH+AnwxFXZGo3k7bjAAgwFnWNm2cY7anZED4pjfFE8x47CA0GhiMA3FGKyoIy5MVg01UVOAYAiai1Kq2OV5gCleU0QaqwOX4uFnQxQmGuwnuiVmqUh7YOrwCmBCyQi4Nx85R7WSvVxvlnFAQbIRsXWnWxvMJykOyhonbhbJmyQ3aSutme71wX3+9A5Eth2FVy3i7Mx3MzfM2apkYByLAl4dlnNV2tnVf60KsSQSIZAttZ8vjvlbndAgBCaYdyuX0s325gSGIcgyKSiKH3I8PdbvEkfz9fogXh+S5jmiVjooMpgAB59qKhgmhwoA+K6YAANYLrQcqsMCTBoZ2XgiOAhxOnUTAvHIkxqpSVFiOhcEhjQWE4bR+HWIRkCRMWcD1AOkw1t+XgEFBBTpBgrqQUJji3JEMRoQAtNJwAxLRckSVBnZoRhjFsDhimyahrGGOxxFcfURQlDAKSUTWNF0emZzadhUkySxVEEQwHEkdxLB7AcRxzD0J4wLu3B8WuAnUQIiSIJZvDsI4LgQAA+q8Mg0GMTCUAAlAA3EI5HyAAdIsRCYFYhVCSJFl6OZpQKu0zRkK02XlWoBREAQOh6A2RCDM2s4oNFCALNlSgAHzePMOg9U2LYoIVA64EwAASRC8HAtLyhEohMFlWXlbcEA6PtAhAA)

#### Filtering on an element in a `list` attribute

```typescript
animals.query
  .exhibit({ habitat: "Tundra" })
  .where(({ offspring }, { eq }) => eq(offspring[0].name, "Blitzen"))
  .go();
```

[![Try it out!](https://img.shields.io/badge/electrodb-try_out_this_example_›-%23f9bd00?style=for-the-badge&logo=amazondynamodb&labelColor=1a212a)](https://electrodb.fun/?ssl=106&ssc=8&pln=103&pc=1#code/JYWwDg9gTgLgBAbwKIDsbBgTwDRwMoCmUAbsAMYEC+cAZlBCHAEQEA2BZM9AJgEZMAoAWQgoAzvACGKUJNZi4AXjgoCAdzip0WABQIBcOCAjc2ALkQHD1sUVIULTAF4QITbFetwCaDJkfSsvLuntbERGLAoo4AjCGGlB6GkjBcwLwArjAEYhb6XgAWkrwYKXmhhlhgBBYA2kwAgnTkku7MACoZKNxQrbhMAEqSwCgAYtA5MEwAuklecFAEAI4ZwIvcFlwZBHMJu94oZKwQYhmL5fNwVTXMElAjAObx1omhgSByF-PXjnePz15Fis1gQNlcoNt9q8vChJCAbvlvphqr80ignqFodZuNIHkQIBlcpZLj9mLxXOxpAC4FjDMAxAAFRYPWFoL5eUlMckQSkoQReWlwEqwAo4-zEpEo25ojEC-bcYAEGDs6yc2xTfZ07IgIlMP7o-kvfYQGg0MRge7olWVZE3JisekaipwDAEHXW1W2xwfMDUrwWiDVWCKomIy6GWHwj0kr3Sy2y8N7Z1eYUwUWScVhxNXWN6mWGxOCy4oCDZaOSu36hOF5M052UTH7cLZS2Se7SD2cn1+uABoPoHLlyMI2s2qV5+MF+ZFmzVMjAORYctjyv52sNy4bo2hViSCSjUGd3NVqeC3f70EAIUzzrVa7lO73MCQxDkGRSURQR-HJ8bj4kL5vh+ojXt+q6TpiVhYiMpgAB6DhKhjmhwoA+B6YAANbLrQiqsGCTCYT2XgiOAJyunUTDvHIMyajStFiFhiHhjQuH4QxRHWCRkCRGWcD1MOMz1n+XgELBBTpBgHowaJjgPJEMSYQAtHJwAxAxinSbBPaYdhLFsPhKkKRhHGGFxZG8fURQlDAKQ0fW9GMVmlx6XhsnyextHEQw3HkXxLCHMcpyLG09pPge3CCZuwl0QIiSIDZvDsI4LgQAA+h8Mg0JMTCUAAlAA3EIVHyAAdCsRCYFYJWieJ1l6FZpTKh0XQ9K0eVVWoBREAQOh6CaZoWo8cUIMseVKAAfN4Sw6P15rxrUAAM0wlcO-SXg6MBOD4TC5blVUPBAOi5UAA)

### Multiple Where Clauses

It is possible to include chain multiple where clauses. The resulting FilterExpressions (or ConditionExpressions) are concatenated with an implicit `AND` operator.

```typescript
let MallStores = new Entity(model, { table: "StoreDirectory" });
let stores = await MallStores.query
  .leases({ mallId: "EastPointe" })
  .between({ leaseEndDate: "2020-04-01" }, { leaseEndDate: "2020-07-01" })
  .where(
    ({ rent, discount }, { between, eq }) => `
		${between(rent, "2000.00", "5000.00")} AND ${eq(discount, "1000.00")}
	`,
  )
  .where(
    ({ category }, { eq }) => `
		${eq(category, "food/coffee")}
	`,
  )
  .go();
```

```json
{
  "TableName": "StoreDirectory",
  "ExpressionAttributeNames": {
    "#rent": "rent",
    "#discount": "discount",
    "#category": "category",
    "#pk": "idx2pk",
    "#sk1": "idx2sk"
  },
  "ExpressionAttributeValues": {
    ":rent1": "2000.00",
    ":rent2": "5000.00",
    ":discount1": "1000.00",
    ":category1": "food/coffee",
    ":pk": "$mallstoredirectory_1#mallid_eastpointe",
    ":sk1": "$mallstore#leaseenddate_2020-04-01#storeid_",
    ":sk2": "$mallstore#leaseenddate_2020-07-01#storeid_"
  },
  "KeyConditionExpression": "#pk = :pk and #sk1 BETWEEN :sk1 AND :sk2",
  "IndexName": "idx2",
  "FilterExpression": "(#rent between :rent1 and :rent2) AND (#discount = :discount1 AND #category = :category1)"
}
```
